#include <jni.h>
#include <string>
#include <android/bitmap.h>
#include <android/log.h>
#include <pthread.h>
#include "HidCallback.h"

#define TAG "myDemo-jni" // 这个是自定义的LOG的标识

#define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...)  __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) // 定义LOGD类型
#define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__) // 定义LOGD类型

static JavaVM *g_jvm = NULL;

//当加载.so库时该方法会被VM自动调用,每个*.so库只能有一个这个入口，即使没有定义的也会有一个默认的方法，
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    g_jvm = vm;//获取一个全局的VM指针
    LOGI("so库加载成功了........>>>>");
    return JNI_VERSION_1_6;
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_hello_jni_DataBus_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++22";
    return env->NewStringUTF(hello.c_str());
}

extern "C" JNIEXPORT jlong JNICALL
Java_com_example_hello_jni_DataBus_getByteBufferPtr(
        JNIEnv *env,
        jclass clazz, jobject byte_buffer) {
    jbyte *cData = (jbyte *) env->GetDirectBufferAddress(byte_buffer);//获取指针
    return (jlong) cData;
}

extern "C" JNIEXPORT jlong JNICALL
Java_com_example_hello_jni_DataBus_getBitmapPixelDataMemoryPtr(
        JNIEnv *env,
        jclass clazz, jobject bitmap) {
    AndroidBitmapInfo bitmapInfo;
    int ret;
    if ((ret = AndroidBitmap_getInfo(env, bitmap, &bitmapInfo)) < 0) {
        LOGE("AndroidBitmap_getInfo() failed ! error=%d", ret);
        return 0;
    }
    // 读取 bitmap 的像素数据块到 native 内存地址
    void *addPtr;
    if ((ret = AndroidBitmap_lockPixels(env, bitmap, &addPtr)) < 0) {
        LOGE("AndroidBitmap_lockPixels() failed ! error=%d", ret);
        return 0;
    }
    //unlock，保证不因这里获取地址导致bitmap被锁定
    AndroidBitmap_unlockPixels(env, bitmap);
    return (jlong) addPtr;
}

/**
 * C++调用java的方法
 */
extern "C"
JNIEXPORT void JNICALL
Java_com_example_hello_jni_DataBus_callJavaMethod(JNIEnv *env, jobject jobj) {
    std::string parm="哈哈";
    //获取java传递的参数
    jstring strCont = env->NewStringUTF(parm.c_str());

    //通过反射调用java中的方法
    //找class 使用FindClass方法，参数就是要调用的函数的类的完全限定名，但是需要把点换成/
    jclass clazz = env->FindClass("com/example/hello/jni/DataBus");
    //获取对应的函数: 参数1:类class,参数2:方法名,参数3:方法签名
    //ps:方法签名的获取:进入build->intermediates->classes->debug目录下,使用javap -s 类的完全限定名,就能获得函数签名
    jmethodID method = env->GetMethodID(clazz, "helloToC",
                                        "(Ljava/lang/String;)Ljava/lang/String;");
    LOGI("调用Java函数");
    //实例化该class对应的实例  使用AllocObject方法，使用clazz创建该class的实例。
    jobject object = env->AllocObject(clazz);
    //调用方法
    jstring result = (jstring) env->CallObjectMethod(object, method,strCont);
    LOGI("调用Java函数结果:%s", env->GetStringUTFChars(result, JNI_FALSE));
}

/**
 * C++ 多线程
 */
#define NUM_THREADS     3

void *PrintHello(void *data) {
    HidCallback *hid_callback = (HidCallback *) data;//在线程中拿到jvm java对象等
    JNIEnv *env;
    //让当前线程与虚拟机联系起来，否则java虚拟机是无法感知C中创建的线程
    g_jvm->AttachCurrentThread(&env, 0);

    LOGI("执行 Thread ID : %d, %s", hid_callback->thread_id, hid_callback->message.c_str());
    //调用方法
    jstring result = (jstring) env->CallObjectMethod(hid_callback->jobj, hid_callback->jmid,
                                                     env->NewStringUTF(
                                                             hid_callback->message.c_str()));
    LOGI("%s", env->GetStringUTFChars(result, JNI_FALSE));

    g_jvm->DetachCurrentThread();
    pthread_exit(NULL);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_example_hello_jni_DataBus_threadMethod(JNIEnv *env, jobject jobj) {
    pthread_t threads[NUM_THREADS];
    int err;
    int i;

    for (i = 0; i < NUM_THREADS; i++) {
        HidCallback *hid_callback = new HidCallback(g_jvm, env, env->NewGlobalRef(jobj));
        env->GetJavaVM(&g_jvm);//保存全局变量

        LOGI("新建线程, %d", i);
        std::string parm = "参数_";
        hid_callback->thread_id = i;
        hid_callback->message = parm + std::to_string(i);

        err = pthread_create(&threads[i], NULL,
                             PrintHello, hid_callback);
        if (err) {
            LOGI("Error:unable to create thread, %d", err);
            exit(-1);
        }
    }
    pthread_exit(NULL);
}